﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing;
using QTPlugin;
using QTPlugin.Interop;
using Kawanon.Usability;
using Kawanon.Usability.Visualization;
using Kawanon.Foundation;
using Kawanon.Localization;

namespace Kawanon.QtHoney
{
	public abstract class Workspace : IDisposable
	{
		private const uint WM_DESTROY = 0x0002;
		private const uint WM_USER = 0x0400;
		private const uint WM_MOUSEMOVE = 0x0200;
		private const uint WM_LBUTTONDOWN = 0x0201;
		private const uint WM_LBUTTONUP = 0x0202;
		private const uint WM_RBUTTONDOWN = 0x0204;
		private const uint WM_RBUTTONUP = 0x0205;
		private const uint WM_MBUTTONDOWN = 0x0207;
		private const uint WM_MBUTTONUP = 0x0208;
		private const uint WM_XBUTTONDOWN = 0x020B;
		private const uint WM_XBUTTONUP = 0x020C;
		private const uint WM_MOUSEWHEEL = 0x020A;
		private const uint WM_CONTEXTMENU = 0x007B;

		private const uint SB_SIMPLE = (WM_USER + 9);
		private const uint SB_SETTEXTW = (WM_USER + 11);

		private const uint SBT_NOBORDERS = 0x0100;

		private const uint HC_NOREMOVE = 3;

		private const uint MK_LBUTTON = 0x0001;
		private const uint MK_RBUTTON = 0x0002;
		private const uint MK_SHIFT = 0x0004;
		private const uint MK_CONTROL = 0x0008;
		private const uint MK_MBUTTON = 0x0010;
		private const uint MK_XBUTTON1 = 0x0020;
		private const uint MK_XBUTTON2 = 0x0040;

		[DllImport("user32.dll")]
		static extern bool EnableWindow(IntPtr hWnd, bool bEnable);

		[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
		static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

		[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
		static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, bool wParam, IntPtr lParam);

		// SB_SETTEXTW
		[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
		static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, UInt32 wParam, string lParam);

		[DllImport("user32.dll")]
		[return: MarshalAs(UnmanagedType.Bool)]
		private static extern bool IsWindow(IntPtr hWnd);

		[DllImport("user32.dll")]
		static extern IntPtr WindowFromPoint(POINT Point);

		[StructLayout(LayoutKind.Sequential)]
		struct MOUSEHOOKSTRUCT
		{
			public POINT pt;
			public IntPtr hwnd;
			public uint wHitTestCode;
			public IntPtr dwExtraInfo;
		}

		protected class PopupMenuStrip : ContextMenuStrip
		{
			public GestureProvider Provider { get; set; }

			protected override void WndProc(ref Message m)
			{
				// ホイールのみ通す
				//（ポップアップをクリックした場合、2度実行されてしまう）
				if (m.Msg == WM_MOUSEWHEEL)
				{
					if (Provider.ProcessMessage(ref m))
					{
						return;
					}
				}
				base.WndProc(ref m);
			}
		}

		public IPluginServer Server { get; set; }
		public IPluginClient Plugin { get; set; }
		public IntPtr StatusBarHandle { get; set; }
		public IShellView ShellView { get; set; }
		public bool DrawTrails
		{
			get
			{
				return m_bDrawTrails;
			}
			set
			{
				//if (value)
				//{
				//    m_provider.Tick += GestureProvider_Tick;
				//}
				//else
				//{
				//    m_provider.Tick -= GestureProvider_Tick;
				//}
				m_bDrawTrails = value;
			}
		}
		public ITrailRenderer Renderer
		{
			get
			{
				return m_renderer;
			}
			set
			{
				if (this.Handle != IntPtr.Zero)
				{
					value.WindowHandle = GetDrawingWindow();
				}
				m_renderer = value;
			}
		}
		public GestureProvider Provider
		{
			get
			{
				return m_provider;
			}
			set
			{
				if (m_provider != null)
				{
					UnregisterProvider(m_provider);
				}
				if (value != null)
				{
					RegisterProvider(value);
				}

				m_provider = value;
			}
		}
		public IViewProxy View { get; set; }
		public IWindowAdapter Adapter
		{
			get
			{
				return m_adapter;
			}
			set
			{
				if (m_adapter != null)
				{
					m_adapter.WindowMessage -= new WindowMessageHandler(ProcessWindowMessage);
				}
				if (value != null)
				{
					value.WindowMessage += new WindowMessageHandler(ProcessWindowMessage);
				}

				m_adapter = value;
			}
		}
		public IntPtr Handle
		{
			get
			{
				return m_handle;
			}
			set
			{
				m_handle = value;

				if (Renderer != null)
				{
					Renderer.WindowHandle = GetDrawingWindow();
				}
			}
		}
		protected bool MouseHooked
		{
			get
			{
				return m_bMouseHooked;
			}
			set
			{
				if (m_bMouseHooked != value)
				{
					WindowHook hk = WindowHook.FromKind(HookKind.WH_MOUSE);
					if (value)
					{
						hk.Captured += new WindowHookHandler(MouseProc);
					}
					else
					{
						hk.Captured -= new WindowHookHandler(MouseProc);
					}
					//Dbg.Play();
				}
				m_bMouseHooked = value;
			}
		}

		protected GestureProvider m_provider = null;
		private bool m_bDrawTrails = false;
		private PopupMenuStrip m_menuStrip = null;
		private WheelExpression m_wheelExpr = null;
		private WheelDirection m_firstDirection;
		private IWindowAdapter m_adapter = null;
		private IntPtr m_handle;
		private ITrailRenderer m_renderer;
		private bool m_bMouseHooked = false;
		private bool m_bGestureDone = false;
		private TriggerButtons m_primaryButton = TriggerButtons.None;
		private bool m_bCancelContextMenu = false;

		public Workspace()
		{
		}

		public void KeepGesture(Workspace view)
		{
			m_menuStrip = view.m_menuStrip;
			m_wheelExpr = view.m_wheelExpr;
			m_firstDirection = view.m_firstDirection;

			// 連続ジェスチャを引き継ぎ、元のフックを解除
			MouseHooked = view.MouseHooked;
			view.MouseHooked = false;

			m_bGestureDone = view.m_bGestureDone;
			m_primaryButton = view.m_primaryButton;
			m_bCancelContextMenu = view.m_bCancelContextMenu;
		}

		private IntPtr GetDrawingWindow()
		{
			if (Server != null &&
				Renderer.RenderingInformation.RenderTarget == RenderTargets.TopLevelWindow)
			{
				return Server.ExplorerHandle;
			}
			else
			{
				return Handle;
			}
		}

		protected virtual void RegisterProvider(GestureProvider provider)
		{
			provider.GestureStart += new GestureEventHandler(GestureProvider_GestureStart);
			provider.GestureDetected += new GestureEventHandler(GestureProvider_GestureDetected);
			provider.TrailDetected += new GestureEventHandler(GestureProvider_TrailDetected);
			provider.GestureCancelled += new GestureEventHandler(GestureProvider_GestureCancelled);
			provider.Tick += GestureProvider_Tick;
			provider.RightClick += new MouseEventHandler(RightClick);

			provider.BeforeWheelGesture += new WheelEventHandler(GestureProvider_BeforeWheelGesture);
			provider.WheelDetected += new WheelEventHandler(GestureProvider_WheelDetected);
			provider.AfterWheelGesture += new WheelEventHandler(GestureProvider_AfterWheelGesture);

			provider.AllowUnknownExpression = true;
		}

		protected virtual void UnregisterProvider(GestureProvider provider)
		{
			provider.GestureStart -= new GestureEventHandler(GestureProvider_GestureStart);
			provider.GestureDetected -= new GestureEventHandler(GestureProvider_GestureDetected);
			provider.TrailDetected -= new GestureEventHandler(GestureProvider_TrailDetected);
			provider.GestureCancelled -= new GestureEventHandler(GestureProvider_GestureCancelled);
			provider.Tick -= GestureProvider_Tick;
			provider.RightClick -= new MouseEventHandler(RightClick);

			provider.BeforeWheelGesture -= new WheelEventHandler(GestureProvider_BeforeWheelGesture);
			provider.WheelDetected -= new WheelEventHandler(GestureProvider_WheelDetected);
			provider.AfterWheelGesture -= new WheelEventHandler(GestureProvider_AfterWheelGesture);
		}

		private MouseButtons MkFlagsToMouseButtons(short mk)
		{
			MouseButtons[] allButtons = new MouseButtons[] {
				MouseButtons.Left,
				MouseButtons.Right,
				MouseButtons.None,
				MouseButtons.None,
				MouseButtons.Middle,
				MouseButtons.XButton1,
				MouseButtons.XButton2 };

			MouseButtons buttons = MouseButtons.None;
			for (int i = 0; i < allButtons.Length; i++)
			{
				int mask = 1 << i;
				if ((mk & mask) == mask)
				{
					buttons = buttons | allButtons[i];
				}
			}
			return buttons;
		}

		private bool ExecuteRockerGesture(TriggerButtons primaryButton, MouseButtons buttons)
		{
			buttons = buttons & ~(MouseButtons)primaryButton;
			if (buttons == MouseButtons.None)
			{
				return false;
			}
			TriggerButtons another = (TriggerButtons)buttons;
			if (!Enum.IsDefined(typeof(TriggerButtons), another))
			{
				// 重複したボタンが押されている
				// FlagsAttribute が含まれていないことが必要
				return false;
			}

			m_bGestureDone = true;

			RockerExpression matchExpr = m_provider.GestureInformation.RockerExpressions.Find(
				(expr) => expr.PrimaryButton == primaryButton && expr.SecondaryButton == another);
			if (matchExpr == null)
			{
				return false;
			}
			if (matchExpr.Action == null)
			{
				return false;
			}

			Execute(matchExpr.Action, QuerySelection(), false);
			return true;
		}

		private bool ExecuteRockerGesture(TriggerButtons primaryButton)
		{
			return ExecuteRockerGesture(primaryButton, Utility.GetPressedMouseButtons());
		}

		private void PrepareRockerGesture(TriggerButtons triggerButton)
		{
			m_primaryButton = triggerButton;
			MouseHooked = true;
			m_bGestureDone = false;
		}

		protected virtual void ProcessWindowMessage(ref Message m)
		{
			switch ((uint)m.Msg)
			{
				case WM_DESTROY:
					Dispose();
					break;

				case WM_RBUTTONUP:
					if (m_bCancelContextMenu)
					{
						m_bCancelContextMenu = false;
						throw new WindowMessageCancelledException();
					}
					break;

				case WM_LBUTTONDOWN:
					PrepareRockerGesture(TriggerButtons.Left);
					break;
				case WM_RBUTTONDOWN:
					// ボタンが再度押された場合はキャンセルしない
					m_bCancelContextMenu = false;

					PrepareRockerGesture(TriggerButtons.Right);
					break;

				case WM_MBUTTONDOWN:
					PrepareRockerGesture(TriggerButtons.Middle);
					break;
				case WM_XBUTTONDOWN:
					int button = Utility.HighWord(m.WParam);
					if (button == 1)
					{
						PrepareRockerGesture(TriggerButtons.XButton1);
					}
					else if (button == 2)
					{
						PrepareRockerGesture(TriggerButtons.XButton2);
					}
					break;
			}

			if (m_provider.ProcessMessage(ref m))
			{
				throw new WindowMessageCancelledException();
			}
		}

		protected virtual void RightClick(object sender, MouseEventArgs e)
		{
		}

		public virtual object QuerySelection()
		{
			return null;
		}

		protected void PerfomRightClick(Point pt, bool buttonUpDown)
		{
			POINT pt2 = new POINT(pt);
			IntPtr hwnd = WindowFromPoint(pt2);
			if (hwnd != this.Handle)
			{
				return;
			}

			IWindowAdapter adapter = this.Adapter;
			this.Adapter = null;
			if (buttonUpDown)
			{
				Utility.ScreenToClient(this.Handle, ref pt2);
				IntPtr pos = (IntPtr)Utility.MakeLong(pt2.x, pt2.y);

				SendMessage(this.Handle, WM_RBUTTONDOWN, (IntPtr)MK_RBUTTON, pos);
				SendMessage(this.Handle, WM_RBUTTONUP, (IntPtr)MK_RBUTTON, pos);
			}
			else
			{
				SendMessage(this.Handle, WM_CONTEXTMENU, this.Handle, Utility.PointToDword(pt));
			}
			this.Adapter = adapter;
		}

		private void ExecuteContinuousGesture(bool reverse)
		{
			GestureExpression expr = Provider.CurrentExpression;
			if (expr != null)
			{
				// モーダルダイアログ対策に先に設定
				m_bGestureDone = true;

				Execute(expr.Action, QuerySelection(), reverse);
			}
			throw new WindowMessageCancelledException();
		}

		private short GetMouseData(IntPtr lParam)
		{
			// MOUSEHOOKSTRUCTEX のメンバを取得
			int mouseData = Marshal.ReadInt32(lParam, Marshal.SizeOf(typeof(MOUSEHOOKSTRUCT)));
			return Utility.HighWord(mouseData);
		}

		private void ProcessContinuousGesture(uint msg, IntPtr lParam)
		{
			if (msg == WM_MOUSEWHEEL)
			{
				short delta = GetMouseData(lParam);
				WheelDirection dir = GestureProvider.WheelDirectionFromDelta(delta);
				bool reverse = false;
				if (m_bGestureDone)
				{
					if (m_firstDirection != WheelDirection.None)
					{
						reverse = dir != m_firstDirection;
					}
				}
				else
				{
					m_firstDirection = dir;
				}
				ExecuteContinuousGesture(reverse);
			}
			else if (msg == WM_LBUTTONDOWN)
			{
				if (!m_bGestureDone)
				{
					m_firstDirection = WheelDirection.None;
				}
				ExecuteContinuousGesture(false);
			}
			else if (msg == WM_LBUTTONUP)
			{
				throw new WindowMessageCancelledException();
			}
		}

		private TriggerButtons ProcessRockerGesture_Internal(uint msg, IntPtr lParam)
		{
			switch (msg)
			{
				case WM_LBUTTONDOWN:
				case WM_RBUTTONDOWN:
				case WM_MBUTTONDOWN:
				case WM_XBUTTONDOWN:
					ExecuteRockerGesture(m_primaryButton);
					break;

					// 主ボタンが離れたら終了
				case WM_LBUTTONUP:
					return TriggerButtons.Left;
				case WM_RBUTTONUP:
					return TriggerButtons.Right;
				case WM_MBUTTONUP:
					return TriggerButtons.Middle;
				case WM_XBUTTONUP:
					short x = GetMouseData(lParam);
					if (x == 1)
					{
						return TriggerButtons.XButton1;
					}
					else if (x == 2)
					{
						return TriggerButtons.XButton2;
					}
					break;
			}
			return TriggerButtons.None;
		}

		private bool ProcessRockerGesture(uint msg, IntPtr lParam)
		{
			TriggerButtons leaveButton = ProcessRockerGesture_Internal(msg, lParam);
			TriggerButtons another = (TriggerButtons)Utility.GetPressedMouseButtons();
			TriggerButtons prevPrimaryButton = m_primaryButton;
			if (another == TriggerButtons.None)
			{
				m_primaryButton = TriggerButtons.None;
				MouseHooked = false;
			}
			else
			{
				// 残った押下ボタンに移す
				m_primaryButton = another;
			}
			if (leaveButton == prevPrimaryButton)
			{
				if (m_bGestureDone)
				{
					// 右クリックメニューを抑止
					// 左クリックは抑止すると選択がおかしくなるのでしない
					if (msg == WM_RBUTTONUP)
					{
						throw new WindowMessageCancelledException();
					}
					else
					{
						m_bCancelContextMenu = true;
					}
				}
				return true;
			}
			return false;
		}

		private bool ShouldIgnore()
		{
			if (Server == null)
			{
				return false;
			}

			object tabBar = Utility.GetBandObject(Server);
			try
			{
				bool modal = (bool)Reflection.GetField(tabBar, "NowModalDialogShown");
				if (modal)
				{
					// ダイアログが開いているならキャンセル
					// 特に ShowGestureSettings を左クリック連続ジェスチャで実行した場合、
					// 無限ループに陥る
					return true;
				}
			}
			catch (MissingFieldException ex)
			{
				Utility.ForException(ex);
			}
			return false;
		}

		private void MouseProc(int code, IntPtr wParam, IntPtr lParam)
		{
			if (code == HC_NOREMOVE)
			{
				return;
			}

			uint msg = (uint)wParam;

			// ホイールメニューが出ているか、または出ようとしている
			if (m_primaryButton != TriggerButtons.None)
			{
				if (msg == WM_MOUSEWHEEL)
				{
					// ビューがアクティブでなくてもジェスチャが使えるように直接渡す
					Provider.ProcessWheelGesture(GetMouseData(lParam));
					throw new WindowMessageCancelledException();
				}
			}

			// ホイールジェスチャのメニューが出ている
			if (m_menuStrip != null)
			{
				if (msg == WM_MOUSEMOVE)
				{
					// カーソル下のメニューが選択されてしまうのを防止
					throw new WindowMessageCancelledException();
				}
				// 左ボタンがドラッグされたままにならないよう全て渡してみる
			//	MOUSEHOOKSTRUCT mhs = (MOUSEHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MOUSEHOOKSTRUCT));
			//	if (mhs.hwnd == m_menuStrip.Handle)
			//	{
					return;
			//	}
			}

			if (ShouldIgnore())
			{
				return;
			}

			if (m_primaryButton != TriggerButtons.None)
			{
				if (m_menuStrip == null)
				{
					if (ProcessRockerGesture(msg, lParam))
					{
						return;
					}
				}
			}
			else
			{
				ProcessContinuousGesture(msg, lParam);

				MOUSEHOOKSTRUCT mhs = (MOUSEHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MOUSEHOOKSTRUCT));
				if (mhs.hwnd == this.Handle)
				{
					// GestureProvider へメッセージを渡す
					return;
				}
			}

			//NOTE: EnableWindow(false) の代わりにここでもみ消す
			switch (msg)
			{
				case WM_LBUTTONDOWN:
				case WM_LBUTTONUP:
				case WM_RBUTTONDOWN:
				case WM_RBUTTONUP:
				case WM_MBUTTONDOWN:
				case WM_MBUTTONUP:
				case WM_XBUTTONDOWN:
				case WM_XBUTTONUP:
				case WM_MOUSEWHEEL:
					//Dbg.Play();
					throw new WindowMessageCancelledException();
			}
		}

		private ToolStripItem FindSelectedItem(ToolStripItemCollection c)
		{
			ToolStripItem[] items = new ToolStripItem[c.Count];
			c.CopyTo(items, 0);

			return Array.Find(items, item => item.Selected);
		}

		void GestureProvider_BeforeWheelGesture(object sender, WheelEventArgs e)
		{
			if (m_menuStrip != null)
			{
				Dbg.Echo("WheelGesture Duplication");
			}

			m_firstDirection = e.Direction;

			m_wheelExpr = m_provider.GestureInformation.WheelExpressions.Find(
				expr => (expr.Button == e.Button) && (expr.Direction & e.Direction) == e.Direction);
			if (m_wheelExpr == null)
			{
				return;
			}

			if (m_wheelExpr.Menu != PopupMenus.None)
			{
				PopupMenuFactory factory = new PopupMenuFactory();
				factory.Server = Server;
				factory.Workspace = this;

				m_menuStrip = new PopupMenuStrip();
				if (Server != null)
				{
					m_menuStrip.Renderer = Server.GetMenuRenderer();
				}
				m_menuStrip.Provider = m_provider;
				m_menuStrip.SuspendLayout();

				factory.CreateItems(m_menuStrip.Items, m_wheelExpr.Menu);

				m_menuStrip.ResumeLayout();

				if (m_menuStrip.Items.Count > 0)
				{
					m_menuStrip.Show(Cursor.Position);
				}
			}
			else if (m_wheelExpr.Action != null)
			{
				Execute(m_wheelExpr.Action, QuerySelection(), false);
			}
		}

		private void MoveSelection(ToolStripItemCollection items, int origin, bool up)
		{
			int index = origin;
			if (up)
			{
				index--;
				if (index < 0)
				{
					index = items.Count - 1;
				}
			}
			else
			{
				index++;
				if (index >= items.Count)
				{
					index = 0;
				}
			}
			if (items[index] is ToolStripSeparator)
			{
				MoveSelection(items, index, up);
			}
			else
			{
				items[index].Select();
			}
		}

		void GestureProvider_WheelDetected(object sender, WheelEventArgs e)
		{
			if (m_wheelExpr == null)
			{
				return;
			}
			if (m_menuStrip != null)
			{
				ToolStripItemCollection items = m_menuStrip.Items;
				ToolStripItem item = FindSelectedItem(items);
				if (item == null)
				{
					if (items.Count > 0)
					{
						items[0].Select();
					}
					return;
				}
				int index = items.IndexOf(item);
				if (e.Direction == WheelDirection.Up)
				{
					MoveSelection(items, index, true);
				}
				else if (e.Direction == WheelDirection.Down)
				{
					MoveSelection(items, index, false);
				}
			}
			else if (m_wheelExpr.Action != null)
			{
				Execute(m_wheelExpr.Action, QuerySelection(), e.Direction != m_firstDirection);
			}
		}

		void GestureProvider_AfterWheelGesture(object sender, WheelEventArgs e)
		{
			if (m_menuStrip != null)
			{
				// アプリケーションを開いた場合に
				// UAC が出ると null になる
				if (m_menuStrip.Items != null)
				{
					ToolStripItem item = FindSelectedItem(m_menuStrip.Items);
					if (item != null)
					{
						item.PerformClick();
					}
				}

				m_menuStrip.Close();
				//m_menuStrip.Dispose();
				m_menuStrip = null;
			}

			m_wheelExpr = null;
		}

		void GestureProvider_Tick(object sender, GestureProvider.NotifyEventArgs e)
		{
			if (DrawTrails)
			{
				Renderer.DrawLine(e.Previous, e.Current);
			}
		}

		protected virtual void OnEnterGesture()
		{
		}

		protected virtual void OnLeaveGesture(bool keepSelection)
		{
		}

		void GestureProvider_GestureStart(object sender, GestureEventArgs e)
		{
			if (DrawTrails)
			{
				Renderer.WindowHandle = GetDrawingWindow();
				Renderer.RenderingInformation.ChangeColor(e.Expression.Button);
				Renderer.SetOrigin(Cursor.Position);
			}

			OnEnterGesture();
		}

		void GestureProvider_GestureCancelled(object sender, GestureEventArgs e)
		{
			if (DrawTrails)
			{
				Renderer.Erase();
			}
		}

		void GestureProvider_TrailDetected(object sender, GestureEventArgs e)
		{
			//if (Server != null)
			//{
			//    //NOTE: ビューの外でボタンを離した際、ReBarなどのメニューを開かないようにする
			//    EnableWindow(Server.ExplorerHandle, false);
			//}
			MouseHooked = true;
			// 連続ジェスチャを実行済みの場合は再開
			m_bGestureDone = false;
			// ロッカージェスチャを無効にする
			m_primaryButton = TriggerButtons.None;

			if (StatusBarHandle != IntPtr.Zero)
			{
				List<GestureExpression> list = new List<GestureExpression>(e.Suggestions);
				GestureExpression equivalent = GestureSettings.FindExpression(list, e.Expression.Trails);
				if (equivalent != null)	// 候補の中に完全にマッチするものがあれば、先頭に表示する
				{
					list.Remove(equivalent);
					list.Insert(0, equivalent);
				}

				int length = e.Expression.Trails.Length;
				List<string> labels = list.ConvertAll(delegate(GestureExpression expr)
				{
					int[] trails = new int[expr.Trails.Length - length];
					Array.Copy(expr.Trails, length, trails, 0, trails.Length);
					if (trails.Length > 0)
					{
						return String.Format(
							StringResource.Localize(MiscResources.SuggestionFormat),
							GestureExpression.FormatTrails(trails),
							expr.Name);
					}
					else
					{
						return expr.Name;
					}
				});
				string text = String.Format("{0}  ({1})",
					e.Expression.FormatTrails(),
					(labels.Count > 0)
						? String.Join(", ", labels.ToArray())
						: StringResource.Localize(MiscResources.Nothing));

				SendMessage(StatusBarHandle, SB_SIMPLE, true, IntPtr.Zero);

				if (SendMessage(StatusBarHandle, SB_SETTEXTW, (255 | SBT_NOBORDERS), text) == IntPtr.Zero)
				{
					Dbg.Abort("SB_SETTEXTW Failed");
				}
			}
		}

		private Action.Arguments Execute(Action action, object target, bool reverse)
		{
			Action.Arguments args = new Action.Arguments();
			args.Server = Server;
			args.Plugin = Plugin;
			args.ShellView = ShellView;
			args.ViewHandle = this.Handle;
			args.Target = target;
			args.Reverse = reverse;
			args.KeepSelection = false;

			if (Server != null)
			{
				try
				{
					action.Execute(args);
				}
				catch (NotImplementedException)
				{
				}
			}
			else
			{
				try
				{
					// とりあえず試行してみる
					action.Execute(args);
				}
				catch (NotImplementedException)
				{
				}
				catch (Exception)
				{
					//MessageBox.Show(this, String.Format("{0} {1} {2}", expr.Name, expr.FormatTrails(), ex.Message));
				}
			}
			return args;
		}

		void GestureProvider_GestureDetected(object sender, GestureEventArgs e)
		{
			//if (Server != null)
			//{
			//    EnableWindow(Server.ExplorerHandle, true);
			//}
			MouseHooked = false;

			// 軌跡を消去
			if (DrawTrails)
			{
				Renderer.Erase();
			}

			bool keepSelection = false;
			if (!m_bGestureDone)
			{
				Action.Arguments ret = Execute(e.Expression.Action, QuerySelection(), false);
				if (!IsWindow(this.Handle))
				{
					// ウィンドウが閉じられた場合は切り上げ
					return;
				}
				keepSelection = ret.KeepSelection;
			}
			OnLeaveGesture(keepSelection);

			if (StatusBarHandle != IntPtr.Zero)
			{
				SendMessage(StatusBarHandle, SB_SIMPLE, false, IntPtr.Zero);
			}
		}

		public virtual void Dispose()
		{
			if (Handle == IntPtr.Zero)
			{
				return;
			}

			if (Adapter != null)
			{
				Adapter.Dispose();
			}
			UnregisterProvider(m_provider);

			if (Renderer != null)
			{
				IDisposable d = Renderer as IDisposable;
				if (d != null)
				{
					d.Dispose();
				}
			}

			Handle = IntPtr.Zero;
		}
	}

	public class WebBrowserWorkspace : Workspace
	{
		/*
		private const uint WM_SETFOCUS = 0x0007;
		private const uint WM_SHOWWINDOW = 0x0018;
		private const uint WM_PAINT = 0x000F;
		private const uint WM_LBUTTONDOWN = 0x0201;
		private const uint WM_LBUTTONUP = 0x0202;
		private const uint WM_RBUTTONDOWN = 0x0204;
		private const uint WM_RBUTTONUP = 0x0205;
		*/

		protected override void RightClick(object sender, MouseEventArgs e)
		{
			PerfomRightClick(e.Location, true);
		}
	}

	public class FolderViewWorkspace : Workspace
	{
		private const uint WM_CONTEXTMENU = 0x007B;
		private const uint WM_LBUTTONDOWN = 0x0201;
		private const uint WM_LBUTTONUP = 0x0202;
		private const uint WM_RBUTTONDOWN = 0x0204;
		private const uint WM_RBUTTONUP = 0x0205;
		private const uint WM_MOUSEMOVE = 0x0200;
		private const uint WM_CAPTURECHANGED = 0x0215;
		private const uint WM_TIMER = 0x0113;
		private const uint WM_CLOSE = 0x0010;
		private const uint WM_DESTROY = 0x0002;
		private const uint WM_USER = 0x0400;

		private const uint S_OK = 0;

		private const uint SVGIO_BACKGROUND = 0;
		private const uint SVGIO_SELECTION = 0x1;
		private const uint SVGIO_ALLVIEW = 0x2;
		private const uint SVGIO_CHECKED = 0x3;
		private const uint SVGIO_TYPE_MASK = 0xf;
		private const uint SVGIO_FLAG_VIEWORDER = 0x80000000;

		[DllImport("user32.dll")]
		static extern IntPtr SetCapture(IntPtr hWnd);

		[DllImport("user32.dll")]
		static extern IntPtr GetCapture();

		[DllImport("user32.dll")]
		static extern bool ReleaseCapture();

		private int m_nOriginItem = -1;

		public FolderViewWorkspace()
		{
		}

		protected override void OnEnterGesture()
		{
			m_nOriginItem = View.HitTest(Cursor.Position, true);
			if (m_nOriginItem != -1)
			{
				//NOTE: 縮小ビューで効かないときがある？

				//NOTE: IFolderView::SelectItem の場合、呼ぶ度に選択状態が
				// クリアされるようなので、この順で呼ぶ必要がある
				View.FocusItem(m_nOriginItem);
				View.SelectItem(m_nOriginItem, true);
			}
		}

		protected override void OnLeaveGesture(bool keepSelection)
		{
			if (!keepSelection && m_nOriginItem != -1)
			{
				//NOTE: 選択した開始点のアイテムを戻す
				try
				{
					View.SelectItem(m_nOriginItem, false);
				}
				catch (OperationFailedException ex)
				{
					//NOTE: Vistaの場合、ウィンドウが破棄されてなくても
					//「更新」などビューが切り替わる場合に FALSE が返る
					Dbg.Abort("Test: " + ex.Message);
				}
			}
		}

		// 右クリックをエミュレート
		protected override void RightClick(object sender, MouseEventArgs e)
		{
			// 最初にすべての選択を解除する
			View.ClearSelection();

			//int count = (int)SendMessage(this.Handle, LVM_GETSELECTEDCOUNT, IntPtr.Zero, IntPtr.Zero);
			//if (count != 0)
			//{
			//    return;
			//}
			int iItem = View.HitTest(e.Location, true);
			if (iItem != -1)
			{
				View.SelectItem(iItem, true);
				View.FocusItem(iItem);
			}

			Application.DoEvents();	// 選択をすぐに切り替える（見かけ上もっさりしないように）

			//Message m = Message.Create(this.Handle, (int)WM_CONTEXTMENU, this.Handle, PointToDword(e.Location));
			//base.WndProc(ref m);
			PerfomRightClick(e.Location, false);
		}

		public override object QuerySelection()
		{
			if (ShellView == null)
			{
				Dbg.Abort("ShellView == null");
				return null;
			}

			Guid IID_IDataObject = new Guid("{0000010e-0000-0000-C000-000000000046}");
			object pv;
			if (ShellView.GetItemObject(SVGIO_SELECTION, ref IID_IDataObject, out pv) != S_OK)
			{
				return null;
			}
			return pv;
		}

		private bool IsItemDragStarted()
		{
			int count = View.GetSelectedCount();
			if (count == 0)
			{
				return false;
			}

			if (!View.HotItemIsSelected())
			{
				return false;
			}

			return true;
		}

		protected override void ProcessWindowMessage(ref Message m)
		{
			switch ((uint)m.Msg)
			{
				case WM_RBUTTONDOWN:
					if (!Provider.GestureInformation.PreferGestureTrack &&
						IsItemDragStarted())
					{
						return;
					}
					break;
			}

			base.ProcessWindowMessage(ref m);
		}
	}
}
